package com.xzz.service.impl;

import com.alibaba.fastjson.JSON;
import com.xzz.domain.AlipayInfo;
import com.xzz.domain.PayFlow;
import com.xzz.domain.PayOrder;
import com.xzz.dto.AlipayNotifyDto;
import com.xzz.dto.ApplyPayDto;
import com.xzz.dto.PayResultDto;
import com.xzz.service.IAlipayInfoService;
import com.xzz.service.IPayFlowService;
import com.xzz.service.IPayOrderService;
import com.xzz.service.IPayService;
import com.xzz.util.AssertUtil;
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.kernel.Config;
import com.alipay.easysdk.kernel.util.ResponseChecker;
import com.alipay.easysdk.payment.page.models.AlipayTradePagePayResponse;
import org.apache.rocketmq.client.producer.LocalTransactionState;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Service
public class PayServiceImpl implements IPayService {

    @Autowired
    private IPayOrderService payOrderService;

    @Autowired
    private IAlipayInfoService alipayInfoService;

    @Autowired
    private IPayFlowService payFlowService;

    @Autowired
    private RocketMQTemplate rocketMQTemplate;


    /**
     * 根据支付的进行支付，与支付宝对接
     * @param dto
     * @return
     */
    @Override
    public String apply(ApplyPayDto dto) {
        PayOrder payOrder = payOrderService.selectByOrderNo(dto.getOrderNo());
        AssertUtil.isNotNull(payOrder,"支付单不存在！！");

        Integer payType = dto.getPayType().intValue();
        // 支付方式:0余额直接，1支付宝，2微信,3银联
        if(payType == 1){//支付宝支付
            return alipay(payOrder,dto);
        }else if (payType == 2){//微信
            return "业务暂未开通";
        }else if (payType == 0){
            return "业务暂未开通";
        }else if (payType == 3){
            return "业务暂未开通";
        }
        return null;
    }


    /**
     * 与支付宝对接流程
     * @param dto
     * @return
     */
    public String alipay(PayOrder payOrder,ApplyPayDto dto){
        AlipayInfo alipayInfo = alipayInfoService.selectList(null).get(0);
        // 1. 设置参数（全局只需设置一次）
        Factory.setOptions(getOptions(alipayInfo));
        try {
            // 2. 发起API调用 网页支付
            AlipayTradePagePayResponse response = Factory.Payment.Page().pay(
                    payOrder.getSubject(),//描述
                    payOrder.getOrderNo(),//订单号
                    payOrder.getAmount().toString(),//金额
                    StringUtils.isEmpty(dto.getCallUrl()) ? alipayInfo.getReturnUrl() : dto.getCallUrl() //同步回调地址
            );
            // 3. 处理响应或异常
            if (ResponseChecker.success(response)) {
                return response.getBody();
            }
            return null;
        } catch (Exception e) {
            System.err.println("调用遭遇异常，原因：" + e.getMessage());
            throw new RuntimeException(e.getMessage(), e);
        }
    }


    /**
     * 设置商家的相关参数
     * 收款方的参数
     * @return
     */
    public Config getOptions(AlipayInfo alipayInfo) {
        Config config = new Config();
        config.protocol = alipayInfo.getProtocol();
        config.gatewayHost = alipayInfo.getGatewayHost();//openapi.alipaydev.com
        config.signType = alipayInfo.getSignType();
        config.appId = alipayInfo.getAppId();
        // 为避免私钥随源码泄露，推荐从文件中读取私钥字符串而不是写入源码中
        config.merchantPrivateKey = alipayInfo.getMerchantPrivateKey();
        //注：如果采用非证书模式，则无需赋值上面的三个证书路径，改为赋值如下的支付宝公钥字符串即可
        config.alipayPublicKey = alipayInfo.getAlipayPublicKey();
        //可设置异步通知接收服务地址（可选）
        config.notifyUrl = alipayInfo.getNotifyUrl();// 后台的支付服务的controller接口
        return config;
    }

    /**
     * 支付完成之后，我们需要更新三个数据库：支付，订单，课程。
     * 支付在本服务里面，直接更新。
     * 订单和课程通过Mq发送事务消息，消费者去广播消费（多消费者用广播）。
     * @param dto
     * @return
     */
    @Override
    public String notify(AlipayNotifyDto dto) {
        //验证签名
        try {
            String jsonString = JSON.toJSONString(dto);//先转成jsonString
            Map map = JSON.parseObject(jsonString, Map.class);//再用jsonString转成任意类型
            Boolean signSuccess = Factory.Payment.Common().verifyNotify(map);
            AssertUtil.isTrue(signSuccess,"验证签名失败");
        } catch (Exception e) {
            e.printStackTrace();
            return "error";
        }
        //验证签名通过，开始校验订单
        PayOrder payOrder = payOrderService.selectByOrderNo(dto.getOut_trade_no());
        AssertUtil.isNotNull(payOrder,"支付单不存在");    //校验支付单
        boolean isEq = payOrder.getAmount().compareTo(new BigDecimal(dto.getTotal_amount())) == 0;//0是相等
        AssertUtil.isTrue(isEq,"支付金额不匹配");         //校验金额是否一致
        AssertUtil.isTrue(dto.isTradeSuccess(), "支付宝交易失败"); //校验支付宝校验是否成功
        AssertUtil.isFalse(dto.isTradeWit(),"交易中，不处理"); //校验是否交易中

        //如果订单已经取消，直接告诉支付宝退款且停止
        if (payOrder.getPayStatus()==PayOrder.STATE_CANCEL){
            try {
                //支付宝退款方法
                Factory.Payment.Common().refund(payOrder.getOrderNo(),payOrder.getAmount().toString());
            } catch (Exception e) {
                e.printStackTrace();
            }
            return "success";
        }

        //发送消息去处理订单和课程，本地事务去处理支付单与流水
        //支付单数据，真正更新在mq的本地事务里面
        Date date = new Date();
        payOrder.setPayStatus(PayOrder.STATE_PAY_SUCCESS);
        payOrder.setUpdateTime(date);

        //支付流水数据，真正添加在mq的本地事务里面
        PayFlow payFlow = new PayFlow();
        payFlow.setNotifyTime(date);
        payFlow.setSubject(payOrder.getSubject());
        payFlow.setOutTradeNo(payOrder.getOrderNo());
        payFlow.setTotalAmount(payOrder.getAmount());
        payFlow.setTradeStatus(dto.getTrade_status());
        payFlow.setPaySuccess(true);

        //支付单与流水的数据放入map交给本地事务处理，也可以用dto
        HashMap<String, Object> payMap = new HashMap<>();
        payMap.put("payOrder",payOrder);
        payMap.put("payFlow",payFlow);

        //处理订单和课程的数据
        PayResultDto resultDto = new PayResultDto(
                payOrder.getOrderNo(),
                payOrder.getExtParams()
        );

        //构建消息去处理订单和课程
        Message<String> message = MessageBuilder.withPayload(JSON.toJSONString(resultDto)).build();

        //发送消息
        TransactionSendResult transactionSendResult = rocketMQTemplate.sendMessageInTransaction(
                "TxPayResultListener",
                "topic-pay:tag-pay",
                message,
                payMap
        );

        //对比本地事务，以及消息，是否发送成功的状态
        LocalTransactionState localTransactionState = transactionSendResult.getLocalTransactionState();
        SendStatus sendStatus = transactionSendResult.getSendStatus();
        boolean isSuccess = localTransactionState.equals(LocalTransactionState.COMMIT_MESSAGE) || sendStatus.equals(SendStatus.SEND_OK);
        AssertUtil.isTrue(isSuccess,"支付失败");

        return "success";
    }


}
